﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Pavilion
{
    public class NotifiableDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
    {
        public int Count { get { return Dictionary.Count; } }
        public IEnumerable<TKey> KeyCollection { get { return Dictionary.Keys; } }
        public NotifiableCollection<TValue> ValueCollection { get; private set; }
        
        private Dictionary<TKey, TValue> Dictionary { get; set; }
        private System.Collections.Specialized.NotifyCollectionChangedEventHandler ValueCollectionChangedHandler { get; set; }

        public NotifiableDictionary()
        {
            Dictionary = new Dictionary<TKey,TValue>();

            //KeyCollection = new NotifiableCollection<TKey>(); If you use this, make sure to add the exceptions for adding and removing items.
            ValueCollection = new NotifiableCollection<TValue>();

            ValueCollectionChangedHandler = (sender, e) =>
            {
                if (e.Action != System.Collections.Specialized.NotifyCollectionChangedAction.Move)
                    throw new InvalidOperationException("This collection is part of the NotifiableDictionary and can not be modified outside of it.");
            };
            ValueCollection.CollectionChanged += ValueCollectionChangedHandler;
        }

        public void Add(TKey key, TValue value)
        {
            Dictionary.Add(key, value);
            AddNotificationItems(key, value);
        }

        public bool Remove(TKey key)
        {
            TValue value = this[key];
            bool returnValue = Dictionary.Remove(key);
            RemoveNotificationItems(key, value);

            return returnValue;
        }

        public void Clear()
        {
            ValueCollection.CollectionChanged -= ValueCollectionChangedHandler;

            Dictionary.Clear();
            ValueCollection.Clear();
            //KeyCollection.Clear();

            ValueCollection.CollectionChanged += ValueCollectionChangedHandler;
        }

        public bool ContainsKey(TKey key)
        {
            return Dictionary.ContainsKey(key);
        }

        public void ReplaceKey(TKey oldKey, TKey newKey)
        {
            TValue value = Dictionary[oldKey];
            Dictionary.Remove(oldKey);
            Dictionary.Add(newKey, value);

            //KeyCollection.Where(x => x.Equals(oldKey)).Select(x => x = newKey); //Test to make sure this works.
        }

        private void AddNotificationItems(TKey key, TValue value)
        {
            ValueCollection.CollectionChanged -= ValueCollectionChangedHandler;

            ValueCollection.Add(value);
            //KeyCollection.Add(key);

            ValueCollection.CollectionChanged += ValueCollectionChangedHandler;
        }

        private void RemoveNotificationItems(TKey key, TValue value)
        {
            ValueCollection.CollectionChanged -= ValueCollectionChangedHandler;

            ValueCollection.Remove(value);
            //KeyCollection.Remove(key);

            ValueCollection.CollectionChanged += ValueCollectionChangedHandler;
        }

        public TValue this[TKey key]
        {
            get { return Dictionary[key]; }
            set
            {
                int valueIndex = ValueCollection.IndexOf(this[key]);

                Dictionary[key] = value;

                ValueCollection.CollectionChanged -= ValueCollectionChangedHandler;
                ValueCollection[valueIndex] = value;
                ValueCollection.CollectionChanged += ValueCollectionChangedHandler;
            }
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return Dictionary.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Dictionary.GetEnumerator();
        }
    }
}
